package com.btr.uimeter.logger;

import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.btr.uimeter.UIMeterEvent;
import com.btr.uimeter.UIMeterEventHandler;
import com.btr.uimeter.probes.UIMeterProbe;

/*****************************************************************************
 * A logger that will write the data in specified intervalls.
 * All data is collected in an internal buffer and then writen as a batch
 * after some time.  
 * @author Bernd Rosstauscher (java@rosstauscher.de) Copyright 2010
 ****************************************************************************/

public class UIMeterCachingHandler implements UIMeterEventHandler {

	private ConcurrentLinkedQueue<EventContext> eventQueue;
	private UIMeterEventHandler delegate;

	private ScheduledExecutorService executorService;
	
	/*************************************************************************
	 * Constructor
	 * @param delegate to use for persisting the data.
	 * @param writeDelay time to wait before persisting all cached events. 
	 * @param timeUnit time unit for the delay.
	 ************************************************************************/

	public UIMeterCachingHandler(UIMeterEventHandler delegate, long writeDelay, TimeUnit timeUnit) {
		super();
		this.delegate = delegate;
		this.eventQueue = new ConcurrentLinkedQueue<EventContext>();
		this.executorService = Executors.newSingleThreadScheduledExecutor();
		this.executorService.scheduleWithFixedDelay(
				new EventWriteTask(), writeDelay, writeDelay, timeUnit);
	}

	/*************************************************************************
	 * processEvent
	 * @see com.btr.uimeter.UIMeterEventHandler#processEvent(UIMeterProbe, UIMeterEvent)
	 ************************************************************************/

	public void processEvent(UIMeterProbe producer, UIMeterEvent e) {
		this.eventQueue.add(new EventContext(producer, e));
	}

	/*************************************************************************
	 * shutdown
	 * @see com.btr.uimeter.UIMeterEventHandler#close()
	 ************************************************************************/

	public void close() {
		this.executorService.shutdown();
		persistCachedEvents();
		this.delegate.close();
	}

	/*************************************************************************
	 * Saves all collected events via the delegate handler.
	 ************************************************************************/
	
	private void persistCachedEvents() {
		Iterator<EventContext> it = this.eventQueue.iterator();
		while (it.hasNext()) {
			EventContext c = it.next();
			delegate.processEvent(c.producer, c.event);
			it.remove();
		}
	}
	
	/*****************************************************************************
	 * Small helper class used by the executor.  
	 ****************************************************************************/
	
	private class EventWriteTask implements Runnable {

		/*************************************************************************
		 * run is used by the internal executer only do not call directly.
		 * @see java.lang.Runnable#run()
		 ************************************************************************/
		
		public void run() {
			persistCachedEvents();
		}
	}
	
	/*****************************************************************************
	 * Internal helper class to store a producer together with an event.
	 ****************************************************************************/
	
	private static class EventContext {
		
		UIMeterProbe producer; 
		UIMeterEvent event;

		/*************************************************************************
		 * Constructor
		 * @param producer to store
		 * @param event to store
		 ************************************************************************/
		
		public EventContext(UIMeterProbe producer, UIMeterEvent event) {
			super();
			this.producer = producer;
			this.event = event;
		}
	}

}